package behaviours.tourguide;

import jade.lang.acl.UnreadableException;
import java.io.IOException;
import java.util.ArrayList;

import resource.Bid;
import agents.TourGuideAgent;
import jade.core.AID;
import jade.core.behaviours.FSMBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.SimpleBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import java.util.Collections;
import java.util.Comparator;

import com.myprofile.profile.ProfileType;

import resource.Tour;


public class NegotiateTourSaleBehaviour extends FSMBehaviour {
    TourGuideAgent tourGuide;
    AID customer;
    ArrayList<Bid> bidStrategy;
    int bidCount;
    Bid latestBid;
    boolean cancelSent = false;

    private final String SEND_PROPOSAL = "A";
    private final String LISTEN_FOR_PROPOSAL_ANSWER = "B";
    private final String SEND_CANCEL = "C";
    private final String SEND_ACCEPT = "D";
    private final String DECIDE_UPON_RECEIVED_COUNTER_PROPOSAL = "E";
    private final String SEND_AGREED_TOUR = "F";
    private final String END_NEGOTIATION = "G";
    private final String DO_CANCEL_NEGOTIATION = "H";
    private final String RECEIVE_AGREED_PROFILER_INFORMATION = "I";

    private final int PROPOSAL_ACCEPTED = 1235423;
    private final int CANCEL_NEGOTIATION = 324324;
    private final int SEND_ACCEPT_PROPOSAL = -32423;
    private final int SEND_CANCEL_PROPOSAL = 234234;

    public NegotiateTourSaleBehaviour(AID customer, TourGuideAgent tourGuide) {
    	if (!tourGuide.customerProfiles.containsKey(customer)){
    		tourGuide.loadTours(tourGuide.customerProfiles.keySet().size()+1);
    	}
        this.customer = customer;
        this.tourGuide = tourGuide;
        createBidStrategy();
        bidCount = 0;



        this.registerFirstState(new SendProposalBehaviour(), SEND_PROPOSAL);
        this.registerState(new SendCancelBehaviour(), SEND_CANCEL);
        this.registerState(new SendAcceptBehaviour(), SEND_ACCEPT);
        this.registerState(new ListenForProposalAnswerBehaviour(), LISTEN_FOR_PROPOSAL_ANSWER);
        this.registerState(new DecideUponCounterProposalBehaviour(), DECIDE_UPON_RECEIVED_COUNTER_PROPOSAL);
        this.registerState(new SendAgreedTourBehaviour(), SEND_AGREED_TOUR);
        this.registerState(new ReceiveAgreedProfilerInformationBehaviour(), RECEIVE_AGREED_PROFILER_INFORMATION);
        this.registerState(new CancelNegotiationBehaviour(), DO_CANCEL_NEGOTIATION);
        this.registerLastState(new EndNegotiateTourSaleBehaviourBehaviour(), END_NEGOTIATION);

        this.registerDefaultTransition(SEND_PROPOSAL, LISTEN_FOR_PROPOSAL_ANSWER);
        this.registerDefaultTransition(LISTEN_FOR_PROPOSAL_ANSWER, DECIDE_UPON_RECEIVED_COUNTER_PROPOSAL);
        this.registerDefaultTransition(DECIDE_UPON_RECEIVED_COUNTER_PROPOSAL, SEND_PROPOSAL);
        this.registerDefaultTransition(SEND_CANCEL, LISTEN_FOR_PROPOSAL_ANSWER);
        this.registerDefaultTransition(SEND_ACCEPT, SEND_AGREED_TOUR);
        this.registerDefaultTransition(SEND_AGREED_TOUR, RECEIVE_AGREED_PROFILER_INFORMATION);
        this.registerDefaultTransition(RECEIVE_AGREED_PROFILER_INFORMATION, END_NEGOTIATION);
        this.registerDefaultTransition(DO_CANCEL_NEGOTIATION, END_NEGOTIATION);
        
        this.registerTransition(SEND_PROPOSAL, SEND_CANCEL, SEND_CANCEL_PROPOSAL);
        this.registerTransition(LISTEN_FOR_PROPOSAL_ANSWER, SEND_AGREED_TOUR, PROPOSAL_ACCEPTED);
        this.registerTransition(LISTEN_FOR_PROPOSAL_ANSWER, DO_CANCEL_NEGOTIATION, CANCEL_NEGOTIATION);
        this.registerTransition(DECIDE_UPON_RECEIVED_COUNTER_PROPOSAL, SEND_ACCEPT, SEND_ACCEPT_PROPOSAL);
        
        
        
    }

    private class SendProposalBehaviour extends OneShotBehaviour {
        int onEnd = 0;

        @Override
        public void action() {
            ACLMessage msg = new ACLMessage(ACLMessage.PROPOSE);
            msg.addReceiver(customer);
            msg.setSender(tourGuide.getAID());
            try {
                if (bidCount < bidStrategy.size()){
                    msg.setContentObject(bidStrategy.get(bidCount));
                    tourGuide.send(msg);
                    System.out.println("Tour GUide: Sent proposal: " + bidStrategy.get(bidCount).P1 + " " + bidStrategy.get(bidCount).P2 + " " + bidStrategy.get(bidCount).P3 + " " + bidStrategy.get(bidCount).tourType);
                    bidCount++;
                } else {
                    onEnd = SEND_CANCEL_PROPOSAL;
                    cancelSent = true;
                }
                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public int onEnd() {
            return onEnd;
        }
        
        
    }
    
    private class SendCancelBehaviour extends OneShotBehaviour {

        @Override
        public void action() {
            ACLMessage msg = new ACLMessage(ACLMessage.CANCEL);
            msg.addReceiver(customer);
            msg.setSender(tourGuide.getAID());
            
            tourGuide.send(msg);
            System.out.println("Tour GUide: Sent cancel");
        }
        
    }
    
    private class SendAcceptBehaviour extends OneShotBehaviour {

        @Override
        public void action() {
            ACLMessage msg = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
            msg.addReceiver(customer);
            msg.setSender(tourGuide.getAID());
            
            tourGuide.send(msg);
            System.out.println("Tour GUide: Sent accept for " + latestBid);
        }
        
    }

    private class ListenForProposalAnswerBehaviour extends SimpleBehaviour {
        private boolean done = false;
        private int onEnd = 0;

        @Override
        public void action() {
            ACLMessage msg = tourGuide.receive(MessageTemplate.MatchSender(customer));
            if (msg != null){
                if (msg.getPerformative() == ACLMessage.ACCEPT_PROPOSAL){
                    onEnd = PROPOSAL_ACCEPTED;
                    latestBid = bidStrategy.get(bidCount-1);
                    System.out.println("Tour GUide: Recived accept latest bid is " + latestBid);
                } else if (msg.getPerformative() == ACLMessage.PROPOSE){
                    try {
                        latestBid = (Bid) msg.getContentObject();
                    } catch (UnreadableException ex) {
                        ex.printStackTrace();
                    }
                    System.out.println("Tour GUide: Recieved propose");
                } else if (msg.getPerformative() == ACLMessage.CANCEL){
                   if (bidCount < bidStrategy.size()){
                        System.out.println("Tour GUide: Recieved cancel continueing bidding anyway");
                   } else {
                        onEnd = CANCEL_NEGOTIATION;
                        System.out.println("Tour GUide: Recieved cancel canceling");
                   }
                }
                done = true;
            } else {
                block();
            }
        }

        @Override
        public boolean done() {
            if (done){
                done = false;
                return true;
            }
            return done;
        }

        @Override
        public int onEnd(){
            System.out.println("Accept: " + (onEnd == PROPOSAL_ACCEPTED));
            return onEnd;
        }
    }
    
    private class DecideUponCounterProposalBehaviour extends OneShotBehaviour {
        int onEnd = 0;
        @Override
        public void action() {
        	boolean interested = true;
               
        	if (latestBid.P1 == true){
        		if (tourGuide.haveCustomerProfileInformation(customer, 1)){
        			interested = false;
        		}
        	}
        	
        	if (latestBid.P2 == true) {
        		if (tourGuide.haveCustomerProfileInformation(customer, 2)){
        			interested = false;
        		}
        	}
        	
            if ((getUtility(latestBid) == getUtility(bidStrategy.get(bidCount))) && interested){
                onEnd = SEND_ACCEPT_PROPOSAL;
            } 
            
            //else do nothing and send another proposal.

        }

        @Override
        public int onEnd() {
//        	if (onEnd != 0){
//        		System.out.println("Tour GUide: Decided to accept bid for: " + latestBid.tourType + ", " + latestBid.P1 +", " + latestBid.P2 + ", " + latestBid.P3);
//        	} else {
//        		System.out.println("Tour GUide: Decided to not accept bid");
//        	}
            return onEnd;
        }
    }
    
    private class SendAgreedTourBehaviour extends OneShotBehaviour {

        @Override
        public void action() {
            ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
            msg.addReceiver(customer);
            msg.setSender(tourGuide.getAID());
            Tour t = null;
            try {
                if (latestBid.tourType == Tour.T1){
                	t = tourGuide.getT1();
                    msg.setContentObject(t);
                } else if (latestBid.tourType == Tour.T2) {
                	t = tourGuide.getT2();
                    msg.setContentObject(t);
                } else {
                	t = tourGuide.getT3();
                    msg.setContentObject(t);
                }
            } catch (IOException e){
                e.printStackTrace();
            }
            
            tourGuide.send(msg);
            System.out.println("tour guide: sent accepted tour " + t.getTourType() + latestBid.tourType);
        }
    }
    
    private class ReceiveAgreedProfilerInformationBehaviour extends SimpleBehaviour {
        boolean done = false;
        @Override
        public void action() {
            ACLMessage msg = tourGuide.receive(MessageTemplate.MatchPerformative(ACLMessage.INFORM));
            if (msg != null){
            	ProfileType pt = null;
            	try {
            		pt = (ProfileType) msg.getContentObject();
            		System.out.println("Received Profile: " + pt);
            	} catch (UnreadableException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				if (tourGuide.customerProfiles.containsKey(customer)){
					tourGuide.mergeProfiles(customer, pt);
				} else {
					tourGuide.customerProfiles.put(customer, pt);
					System.out.println("Received Profile: " + pt);
				}
				
				System.out.println("tour guide: recived profiler info");
                                done = true;
            } else {
                block();
                System.out.println("Didn't get the profileinfo!!");
            }
        }

        @Override
        public boolean done() {
            return done;
        }
        
    }
    
    private class CancelNegotiationBehaviour extends OneShotBehaviour {

        @Override
        public void action() {
            if (!cancelSent){
                ACLMessage msg = new ACLMessage(ACLMessage.CANCEL);
                msg.addReceiver(customer);
                msg.setSender(tourGuide.getAID());
                tourGuide.send(msg);
            }
            System.out.println("tour guide: canceled negotiation");
        }
    }
    
    private void createBidStrategy(){
        bidStrategy = new ArrayList<Bid>();
        if (!tourGuide.haveCustomerProfileInformation(customer, 1)){
            bidStrategy.add(new Bid(true,false,45, Tour.T1));
            bidStrategy.add(new Bid(true,false,30, Tour.T1));
            bidStrategy.add(new Bid(true,false,15, Tour.T1));
            bidStrategy.add(new Bid(true,false,45, Tour.T2));
            bidStrategy.add(new Bid(true,false,30, Tour.T2));
            bidStrategy.add(new Bid(true,false,0, Tour.T1));
            bidStrategy.add(new Bid(true,false,45, Tour.T3));
            bidStrategy.add(new Bid(true,false,30, Tour.T3));
        }
        
        if (!tourGuide.haveCustomerProfileInformation(customer, 2)){
            bidStrategy.add(new Bid(false,true,45, Tour.T1));
            bidStrategy.add(new Bid(false,true,30, Tour.T1));
            bidStrategy.add(new Bid(false,true,15, Tour.T1));
            bidStrategy.add(new Bid(false,true,45, Tour.T2));
            bidStrategy.add(new Bid(false,true,30, Tour.T2));
            bidStrategy.add(new Bid(false,true,15, Tour.T2));
            bidStrategy.add(new Bid(false,true,45, Tour.T3));
            bidStrategy.add(new Bid(false,true,30, Tour.T3));
            bidStrategy.add(new Bid(false,true,15, Tour.T3));
        }
        
        if (!tourGuide.haveCustomerProfileInformation(customer, 1) &&
                !tourGuide.haveCustomerProfileInformation(customer, 2)){
            bidStrategy.add(new Bid(true,true,45, Tour.T1));
            bidStrategy.add(new Bid(true,true,30, Tour.T1));
            bidStrategy.add(new Bid(true,true,15, Tour.T1));
            bidStrategy.add(new Bid(true,true,45, Tour.T2));
            bidStrategy.add(new Bid(true,true,30, Tour.T2));
            bidStrategy.add(new Bid(true,true,15, Tour.T2));
            bidStrategy.add(new Bid(true,true,45, Tour.T3));
            bidStrategy.add(new Bid(true,true,30, Tour.T3));
            bidStrategy.add(new Bid(true,true,15, Tour.T3));
            bidStrategy.add(new Bid(true,true,0, Tour.T1));
            bidStrategy.add(new Bid(true,true,0, Tour.T2));
        }

        bidStrategy.add(new Bid(false,false,45, Tour.T1));
        bidStrategy.add(new Bid(false,false,30, Tour.T1));
        bidStrategy.add(new Bid(false,false,15, Tour.T1));
        bidStrategy.add(new Bid(false,true,0, Tour.T1));
        bidStrategy.add(new Bid(false,false,45, Tour.T2));
        bidStrategy.add(new Bid(false,false, 45, Tour.T3));

        
        Collections.sort(bidStrategy, new BidComparator());
        
        printBidStrategy();
        System.out.println("tour guide: I'm going to try " + bidStrategy.size() + " bids");
    }
    
    private void printBidStrategy(){
        for (int i = 0; i < bidStrategy.size(); i++){
            System.out.println("Utility: " + getUtility(bidStrategy.get(i)) + ", " + bidStrategy.get(i));
        }
    }
    
    private class EndNegotiateTourSaleBehaviourBehaviour extends OneShotBehaviour {

        @Override
        public void action() {
            System.out.println("Negotiation ended");
        }
        
    }
    
    	public class BidComparator implements Comparator<Bid>{

            @Override
            public int compare(Bid b1, Bid b2) {
                int b1Utility = getUtility(b1);
                int b2Utility = getUtility(b2);


                if(b1Utility > b2Utility) {
                    return -1;
                }
                else if(b1Utility < b2Utility) {
                    return 1;
                }
                else {
                    return 0;
                }
            }
	}
	
	private int getUtility(Bid b) {	
            int P1 = b.P1 ? 1 : 0;
            int P2 = b.P2 ? 1 : 0;
            int price = (P1*10 + P2*25 + b.P3);
            if(b.tourType == Tour.T1) {
                return price-10;
            }
            else if(b.tourType == Tour.T2) {
                return price-35;
            }
            else {
                return price-40;
            }
	}
}
